#encoding=utf-8
import socket
import select


#创建套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

localAddr = ('', 7788)
s.bind(localAddr)

s.setblocking(False)

s.listen(10)



#创建一个epoll对象
epoll = select.epoll()



#注册事件到epoll中
#epoll.register(fd[,eventmask])
#注意，如果发的已经注册过了，则会发生异常
#讲创建的套接字添加到epoll事件监听中
epoll.register(s.fileno(), select.EPOLLIN|select.EPOLLET)
#EPOLLIN （可读）,EPOLLOUT （可写）,EPOLLET （ET模式）
#epoll对文件描述符的操作有两种模式：LT（level trigger）和ET（edge trigger）。LT模式是默认模式，LT模式与ET模式的区别如下：
#LT模式：当epoll检测到描述符事件发生并将此事件通知应用程序，应用程序可以不立即处理该事件。下次调用epoll时，会再次响应应用程序并通知此事件。

#ET模式：当epoll检测到描述符事件发生并将此事件通知应用程序，应用程序必须立即处理该事件。如果不处理，下次调用epoll时，不会再次响应应用程序并通知此事件。
connections = {}
addresses = {}


#循环等待客户端的到来或者对方发送数据
while True:
    #epoll进行fd扫描的地方，未指定超时事件则未阻塞等待
    epoll_list = epoll.poll()

    #对事件进行判断
    for fd,events in epoll_list:
        #如果是socket创建的套接字被激活
        if fd == s.fileno():
            conn,addr = s.accept()
            print "有新的客户端到来%s"%str(addr)
            #将conn和addr信息分别保存起来
            connections[conn.fileno()] = conn
            addresses[conn.fileno()] = addr

            #向epoll中注册链接socket的刻度事件
            epoll.register(conn.fileno(), select.EPOLLIN | select.EPOLLET)

        elif events == select.EPOLLIN:
            #从激活fd上接收
            recvData = connections[fd].recv(1024)
            if len(recvData) > 0:
                print "recv%s"%recvData
            else:
                #从epoll移除该链接fd
                epoll.unregister(fd)
                #server则主动关闭该链接fd
                connections[fd].close()


                print "%s----offline----"%str(addresses[fd])


